package han.cirno.corrupt.components.school.impl.fjut

import han.cirno.corrupt.R
import han.cirno.corrupt.components.school.SchoolProvider
import han.cirno.corrupt.components.school.class_time.ClassTime
import han.cirno.corrupt.components.school.class_time.SimpleClassTime
import han.cirno.corrupt.components.school.impl.mju.MjuClassResolver
import han.cirno.corrupt.components.school.impl.mju.MjuScoreResolver
import han.cirno.corrupt.components.school.term_time.BasicTermTime
import han.cirno.corrupt.components.school.term_time.TermTime
import han.cirno.corrupt.data.DB
import han.cirno.corrupt.data.instances.UserClassesData
import han.cirno.corrupt.util.ViewUtil
import org.jsoup.Jsoup

class FjutSchool: SchoolProvider {
    companion object{
        val supportedInfo= arrayOf(
            SchoolProvider.INFO_CLASSES,
            SchoolProvider.INFO_EXAMS,
            SchoolProvider.INFO_SCORE
        )
        val fjutClassTime= SimpleClassTime.Builder()
            .add(8,20)
            .add(9,15)
            .add(10,15)
            .add(11,5)
            .add(14,0)
            .add(14,55)
            .add(15,55)
            .add(16,50)
            .add(18,30)
            .add(19,25)
            .add(20,20)
            .build()
        val termTime= BasicTermTime()
    }

    override fun getSchoolName(): String = "Fujian University of Technology"
    override fun getTermTime(): TermTime = termTime
    override fun getClassTime(): ClassTime = fjutClassTime
    override fun hasInfo(infoKey: String): Boolean = supportedInfo.contains(infoKey)

    override fun getString(key: String): String? {
        return when(key){
            SchoolProvider.STRING_INFO -> {
                val dataOperation = dataOperation()
                ViewUtil.getString(
                    resId = R.string.card_personalInfo_logged_fafu,
                    paras = arrayOf(
                        getSchoolName(),
                        dataOperation.singleGet(SchoolProvider.KEY_ID,String::class)!!,
                        dataOperation.singleGet(SchoolProvider.KEY_ID,String::class)!!,
                        "(Nullity)"))
            }
            else->null
        }
    }

    override suspend fun performLogin(
        userName: String,
        password: String,
        callback: SchoolProvider.LoginCallback
    ): Result<String> {
        dataOperation()
            .write(SchoolProvider.KEY_ID, userName)
            .write(SchoolProvider.KEY_PASSWORD, password)
            .save()
        return FjutEduClient.instance.request(null,callback)
    }

    override suspend fun fetchInfo(infoKey: String, vararg params: Any?): Result<Any> {
        return when(infoKey) {
            SchoolProvider.INFO_CLASSES ->fetchClassesTable()
            SchoolProvider.INFO_EXAMS-> fetchExamInfo()
            SchoolProvider.INFO_SCORE-> fetchScoreInfo()
            else -> Result.failure(IllegalArgumentException("Not a valid info key!"))
        }
    }

    private fun fetchScoreInfo():Result<Unit>{
        var xnm=""
        var xqm=""
        FjutEduClient.instance.request(FjutEduConst.Payload.Score)
            .onSuccess {
                try {
                    if (it.isEmpty()) {
                        return Result.failure(Exception("Empty response body"))
                    }
                    val classPageDoc = Jsoup.parse(it)
                    val xnmElement = classPageDoc.getElementById("xnm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    val xqmElement = classPageDoc.getElementById("xqm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    xnm = xnmElement.attr("value")
                    xqm = xqmElement.attr("value")
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        FjutEduClient.instance.request(FjutEduConst.Payload.ScoreData(xnm, xqm))
            .onSuccess {
                val scoreDao= DB.getInstance().scoreDao()
                scoreDao.deleteAll()
                scoreDao.insertAll(MjuScoreResolver.analyse(it))
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchExamInfo():Result<Unit>{
        var xnm=""
        var xqm=""
        FjutEduClient.instance.request(FjutEduConst.Payload.Exam)
            .onSuccess {
                try {
                    if (it.isEmpty()) {
                        return Result.failure(Exception("Empty response body"))
                    }
                    val classPageDoc = Jsoup.parse(it)
                    val xnmElement = classPageDoc.getElementById("cx_xnm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    val xqmElement = classPageDoc.getElementById("cx_xqm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    xnm = xnmElement.attr("value")
                    xqm = xqmElement.attr("value")
                }catch (e:Exception){
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        FjutEduClient.instance.request(FjutEduConst.Payload.ExamData(xnm, xqm))
            .onSuccess {
                val examDao= DB.getInstance().examDao()
                try{
                    examDao.deleteAll()
                    examDao.insertAll(FjutExamResolver.analyze(it))
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchClassesTable():Result<Any>{
        var xnm=""
        var xqm=""
        var xnmReadable=""
        var xqmReadable=""
        val classTableDao = DB.getInstance().classTableDao()
        val classEntryDao = DB.getInstance().classEntryDao()
        FjutEduClient.instance.request(FjutEduConst.Payload.ClassesTable)
            .onSuccess{
                if (it.isEmpty()){
                    return Result.failure(Exception("Empty response body"))
                }
                val classPageDoc = Jsoup.parse(it)
                val xnmElement = classPageDoc.getElementById("xnm")!!
                    .getElementsByAttributeValue("selected", "selected")
                val xqmElement = classPageDoc.getElementById("xqm")!!
                    .getElementsByAttributeValue("selected", "selected")
                xnm = xnmElement.attr("value")
                xqm = xqmElement.attr("value")
                xnmReadable=xnmElement.text()
                xqmReadable=xqmElement.text()
            }.onFailure {
                return Result.failure(it)
            }
        FjutEduClient.instance.request(FjutEduConst.Payload.ClassTableData(xnm, xqm))
            .onSuccess {
                UserClassesData.makeDirty()
                classTableDao.deleteAll()
                classEntryDao.deleteAll()
                try{
                    val classTable = MjuClassResolver.analyze(it, xnmReadable, xqmReadable)
                    classTableDao.insert(classTable)
                    classEntryDao.insertAll(classTable.classEntries)
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }
}